using System;
using System.Text;

namespace ICommonLibrary.Security.GnuPG {

    public class GPGOptions {

        public GPGCommands Command { get; set; }

        public bool AlwaysTrust { get; set; }

        public bool TextMode { get; set; }

        /// <summary>
        /// Boolean flag: if true, GnuPG creates ASCII armored output (text output). 
        /// 
        /// <p/>Defaults to true (ASCII ouput).
        /// </summary>
        public bool Armor { get; set; }

        /// <summary>
        /// Recipient email address - mandatory when <see cref="Command">command</see> is Encrypt or SignAndEncrypt
        /// 
        /// <p/>GnuPG uses this parameter to find the associated public key. You must have imported 
        /// this public key in your keyring before.
        /// </summary>
        public string Recipient { get; set; }

        /// <summary>
        /// Originator email address - recommended when <see cref="Command">command</see> is Sign or SignAndEncrypt
        /// 
        /// <p/>GnuPG uses this parameter to find the associated secret key. You must have imported 
        /// this secret key in your keyring before. Otherwise, GnuPG uses the first secret key 
        /// in your keyring to sign messages. This property is mapped to the "--default-key" option.
        /// </summary>
        public string Originator { get; set; }

        /// <summary>
        /// Boolean flag; if true, GnuPG assumes "yes" on most questions.
        /// 
        /// <p/>Defaults to true.
        /// </summary>
        public bool Yes { get; set; }

        /// <summary>
        /// Boolean flag; if true, GnuPG uses batch mode (Never ask, do not allow 
        /// interactive commands).
        /// 
        /// <p/>Defaults to true.
        /// </summary>
        public bool Batch { get; set; }

        /// <summary>
        /// Passphrase for using your private key - mandatory when 
        /// <see cref="Command">command</see> is Sign or SignAndEncrypt.
        /// </summary>
        public string Passphrase {
            get {
                return _passphrase;
            }            
            set {
                _passphrase = value;
                if (_passphrase != "") {
                    Passphrasefd = "0"; // stdin
                }
                else {
                    Passphrasefd = "";
                }
            }
        }

        /// <summary>
        /// Path of the home directory where keyrings is located
        /// </summary>
        public string HomeDirectory { get; set; }

        /// <summary>
        /// Path of the bin directory where gpg.exe is located
        /// </summary>
        public string BinDirectory { get; set; }

        /// <summary>
        /// File descriptor for entering passphrase - defaults to 0 (standard input).
        /// </summary>
        public string Passphrasefd { get; set; }

        /// <summary>
        /// Verbose level (NoVerbose, Verbose, VeryVerbose). 
        /// 
        /// <p/>Defaults to NoVerbose.
        /// </summary>
        public GPGVerboseLevel Verbose { get; set; }


        private string _passphrase = "";

        public GPGOptions() {
            AlwaysTrust = true;
            Armor = true;
            Yes = true;
            Batch = true;
            Passphrase = "";
            Verbose = GPGVerboseLevel.NoVerbose;
        }

        /// <summary>
        /// Generate a string of GnuPG command line arguments, based on the properties
        /// set in this object (e.g. if the <see cref="Armor">armor</see> property is true, 
        /// this method generates the "--armor" argument).
        /// </summary>
        /// <returns>GnuPG command line arguments</returns>
        public string BuildOptions() {
            var optionsBuilder = new StringBuilder("", 255);
            bool recipientNeeded = false;
            bool passphraseNeeded = false;

            // Home Directory?
            if (!string.IsNullOrEmpty(HomeDirectory)) {
                optionsBuilder.Append("--homedir=\"");
                optionsBuilder.Append(HomeDirectory);
                optionsBuilder.Append("\" ");
            }

            if (AlwaysTrust) {
                optionsBuilder.Append("--always-trust ");
            }

            // Answer yes to all questions?
            if (Yes) {
                optionsBuilder.Append("--yes ");
            }

            // batch mode?
            if (Batch) {
                optionsBuilder.Append("--batch ");
            }

            // Command
            switch (Command) {
                case GPGCommands.Sign:
                    optionsBuilder.Append("--sign ");
                    passphraseNeeded = true;
                    break;
                case GPGCommands.Encrypt:
                    optionsBuilder.Append("--encrypt ");
                    recipientNeeded = true;
                    break;
                case GPGCommands.SignAndEncrypt:
                    optionsBuilder.Append("--sign ");
                    optionsBuilder.Append("--encrypt ");
                    recipientNeeded = true;
                    passphraseNeeded = true;
                    break;
                case GPGCommands.Decrypt:
                    optionsBuilder.Append("--decrypt ");
                    break;
                case GPGCommands.Verify:
                    optionsBuilder.Append("--verify ");
                    break;
            }

            // ASCII output?
            if (Armor) {
                optionsBuilder.Append("--armor ");
            }

            if (TextMode) {
                optionsBuilder.Append("--text ");
            }

            // Recipient?
            if (!string.IsNullOrEmpty(Recipient)) {
                optionsBuilder.Append("--recipient=\"");
                optionsBuilder.Append(Recipient);
                optionsBuilder.Append("\" ");
            }
            else {
                // If you encrypt, you NEED a recipient!
                if (recipientNeeded) {
                    throw new ArgumentException("GPG: Missing 'recipient' parameter: cannot encrypt without a recipient");
                }
            }

            // Originator?
            if (!string.IsNullOrEmpty(Originator)) {
                optionsBuilder.Append("--default-key ");
                optionsBuilder.Append(Originator);
                optionsBuilder.Append(" ");
            }

            // Passphrase?
            if (string.IsNullOrEmpty(_passphrase)) {
                if (passphraseNeeded) {
                    throw new ArgumentException("GPG: Missing 'passphrase' parameter: cannot sign without a passphrase");
                }
            }

            // Passphrase file descriptor?
            if (!string.IsNullOrEmpty(Passphrasefd)) {
                optionsBuilder.Append("--passphrase-fd ");
                optionsBuilder.Append(Passphrasefd);
                optionsBuilder.Append(" ");
            }
            else {
                if (passphraseNeeded && (_passphrase == null || _passphrase == "")) {
                    throw new ArgumentException("GPG: Missing 'passphrase' parameter: cannot sign without a passphrase");
                }
            }

            // Command
            switch (Verbose) {
                case GPGVerboseLevel.NoVerbose:
                    optionsBuilder.Append("--no-verbose ");
                    break;
                case GPGVerboseLevel.Verbose:
                    optionsBuilder.Append("--verbose ");
                    break;
                case GPGVerboseLevel.VeryVerbose:
                    optionsBuilder.Append("--verbose --verbose ");
                    break;
            }

            return optionsBuilder.ToString();
        }
    }
}